{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Getting started"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First things first, make sure you have [installed river](installation.md).\n",
    "\n",
    "In `river`, features are represented with dictionaries, where the keys correspond to the features names. For instance:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.055229Z",
     "iopub.status.busy": "2020-11-25T21:22:56.054720Z",
     "iopub.status.idle": "2020-11-25T21:22:56.059326Z",
     "shell.execute_reply": "2020-11-25T21:22:56.058874Z"
    }
   },
   "outputs": [],
   "source": [
    "import datetime as dt\n",
    "\n",
    "x = {\n",
    "    'shop': 'Ikea',\n",
    "    'city': 'Stockholm',\n",
    "    'date': dt.datetime(2020, 6, 1),\n",
    "    'sales': 42\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is up to you, the user, to decide how to stream your data. `river` offers a `stream` module which has various utilities for handling streaming data, such as `stream.iter_csv`. For the sake of example, `river` also provides a `datasets` module which contains various streaming datasets. For example, the `datasets.Phishing` dataset contains records of [phishing](https://www.wikiwand.com/en/Phishing) attempts on web pages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.062425Z",
     "iopub.status.busy": "2020-11-25T21:22:56.061968Z",
     "iopub.status.idle": "2020-11-25T21:22:56.821103Z",
     "shell.execute_reply": "2020-11-25T21:22:56.821527Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Phishing websites.\n",
      "\n",
      "This dataset contains features from web pages that are classified as phishing or not.\n",
      "\n",
      "    Name  Phishing                                                    \n",
      "    Task  Binary classification                                       \n",
      " Samples  1,250                                                       \n",
      "Features  9                                                           \n",
      "  Sparse  False                                                       \n",
      "    Path  /home/runner/work/river/river/river/datasets/phishing.csv.gz\n"
     ]
    }
   ],
   "source": [
    "from river import datasets\n",
    "\n",
    "dataset = datasets.Phishing()\n",
    "print(dataset)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The dataset is a streaming dataset, and therefore doesn't sit in memory. Instead, we can loop over each sample with a `for` loop:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.831962Z",
     "iopub.status.busy": "2020-11-25T21:22:56.824497Z",
     "iopub.status.idle": "2020-11-25T21:22:56.836193Z",
     "shell.execute_reply": "2020-11-25T21:22:56.835792Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'empty_server_form_handler': 1.0, 'popup_window': 0.5, 'https': 1.0, 'request_from_other_domain': 1.0, 'anchor_from_other_domain': 1.0, 'is_popular': 0.5, 'long_url': 0.0, 'age_of_domain': 0, 'ip_in_url': 0}\n"
     ]
    }
   ],
   "source": [
    "for x, y in dataset:\n",
    "    pass\n",
    "\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.840663Z",
     "iopub.status.busy": "2020-11-25T21:22:56.839465Z",
     "iopub.status.idle": "2020-11-25T21:22:56.842276Z",
     "shell.execute_reply": "2020-11-25T21:22:56.841864Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Typically, models learn via a `learn_one(x, y)` method, which takes as input some features and a target value. Being able to learn with a single instance gives a lot of flexibility. For instance, a model can be updated whenever a new sample arrives from a stream. To exemplify this, let's train a logistic regression on the above dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.845598Z",
     "iopub.status.busy": "2020-11-25T21:22:56.845139Z",
     "iopub.status.idle": "2020-11-25T21:22:56.877909Z",
     "shell.execute_reply": "2020-11-25T21:22:56.878321Z"
    }
   },
   "outputs": [],
   "source": [
    "from river import linear_model\n",
    "\n",
    "model = linear_model.LogisticRegression()\n",
    "\n",
    "for x, y in dataset:\n",
    "    model.learn_one(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Predictions can be obtained by calling a model's `predict_one` method. In the case of a classifier, we can also use `predict_proba_one` to produce probability estimates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.881792Z",
     "iopub.status.busy": "2020-11-25T21:22:56.881336Z",
     "iopub.status.idle": "2020-11-25T21:22:56.919105Z",
     "shell.execute_reply": "2020-11-25T21:22:56.919484Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{False: 0.7731541581376543, True: 0.22684584186234572}\n"
     ]
    }
   ],
   "source": [
    "model = linear_model.LogisticRegression()\n",
    "\n",
    "for x, y in dataset:\n",
    "    y_pred = model.predict_proba_one(x)\n",
    "    model.learn_one(x, y)\n",
    "    \n",
    "print(y_pred)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `metrics` module gives access to many metrics that are commonly used in machine learning. Like the rest of `river`, these metrics can be updated with one element at a time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:56.923883Z",
     "iopub.status.busy": "2020-11-25T21:22:56.923128Z",
     "iopub.status.idle": "2020-11-25T21:22:57.182010Z",
     "shell.execute_reply": "2020-11-25T21:22:57.182375Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ROCAUC: 0.893565"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from river import metrics\n",
    "\n",
    "model = linear_model.LogisticRegression()\n",
    "\n",
    "metric = metrics.ROCAUC()\n",
    "\n",
    "for x, y in dataset:\n",
    "    y_pred = model.predict_proba_one(x)\n",
    "    model.learn_one(x, y)\n",
    "    metric.update(y, y_pred)\n",
    "    \n",
    "metric"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A common way to improve the performance of a logistic regression is to scale the data. This can be done by using a `preprocessing.StandardScaler`. In particular, we can define a pipeline to organise our model into a sequence of steps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:57.190762Z",
     "iopub.status.busy": "2020-11-25T21:22:57.186008Z",
     "iopub.status.idle": "2020-11-25T21:22:57.208182Z",
     "shell.execute_reply": "2020-11-25T21:22:57.207789Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.40.1 (20161225.0304)\n",
       " -->\n",
       "<!-- Title: %3 Pages: 1 -->\n",
       "<svg width=\"165pt\" height=\"260pt\"\n",
       " viewBox=\"0.00 0.00 165.29 260.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 256)\">\n",
       "<title>%3</title>\n",
       "<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-256 161.286,-256 161.286,4 -4,4\"/>\n",
       "<!-- x -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>x</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"78.643\" cy=\"-234\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"78.643\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">x</text>\n",
       "</g>\n",
       "<!-- StandardScaler -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>StandardScaler</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"78.643\" cy=\"-162\" rx=\"63.8893\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"78.643\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">StandardScaler</text>\n",
       "</g>\n",
       "<!-- x&#45;&gt;StandardScaler -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>x&#45;&gt;StandardScaler</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M78.643,-215.8314C78.643,-208.131 78.643,-198.9743 78.643,-190.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"82.1431,-190.4132 78.643,-180.4133 75.1431,-190.4133 82.1431,-190.4132\"/>\n",
       "</g>\n",
       "<!-- LogisticRegression -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>LogisticRegression</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"78.643\" cy=\"-90\" rx=\"78.7863\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"78.643\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">LogisticRegression</text>\n",
       "</g>\n",
       "<!-- StandardScaler&#45;&gt;LogisticRegression -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>StandardScaler&#45;&gt;LogisticRegression</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M78.643,-143.8314C78.643,-136.131 78.643,-126.9743 78.643,-118.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"82.1431,-118.4132 78.643,-108.4133 75.1431,-118.4133 82.1431,-118.4132\"/>\n",
       "</g>\n",
       "<!-- y -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>y</title>\n",
       "<ellipse fill=\"none\" stroke=\"#000000\" cx=\"78.643\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"78.643\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">y</text>\n",
       "</g>\n",
       "<!-- LogisticRegression&#45;&gt;y -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>LogisticRegression&#45;&gt;y</title>\n",
       "<path fill=\"none\" stroke=\"#000000\" d=\"M78.643,-71.8314C78.643,-64.131 78.643,-54.9743 78.643,-46.4166\"/>\n",
       "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"82.1431,-46.4132 78.643,-36.4133 75.1431,-46.4133 82.1431,-46.4132\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.dot.Digraph at 0x7f8e102d61f0>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from river import compose\n",
    "from river import preprocessing\n",
    "\n",
    "model = compose.Pipeline(\n",
    "    preprocessing.StandardScaler(),\n",
    "    linear_model.LogisticRegression()\n",
    ")\n",
    "\n",
    "model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2020-11-25T21:22:57.340265Z",
     "iopub.status.busy": "2020-11-25T21:22:57.211873Z",
     "iopub.status.idle": "2020-11-25T21:22:57.535995Z",
     "shell.execute_reply": "2020-11-25T21:22:57.535555Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ROCAUC: 0.950363"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "metric = metrics.ROCAUC()\n",
    "\n",
    "for x, y in datasets.Phishing():\n",
    "    y_pred = model.predict_proba_one(x)\n",
    "    model.learn_one(x, y)\n",
    "    metric.update(y, y_pred)\n",
    "    \n",
    "metric"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As we can see, the model is performing much better now that the data is being scaled. Under the hood, the standard scaler maintains a running average and a running variance for each feature in the dataset. Each feature is thus scaled according to the average and the variance seen up to every given point in time.\n",
    "\n",
    "This concludes this short guide to getting started with `river`. There is a lot more to discover and understand. Head towards the [user guide](../user-guide/reading-data.md) for recipes on how to perform common machine learning tasks. You may also consult the [API reference](../api/overview.md), which is a catalogue of all the modules that `river` exposes. Finally, the [examples](../examples/batch-to-online.md) section contains comprehensive examples for various usecases."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
